package com.googlecode.sere.matcher

import scala.util.matching.Regex
import com.googlecode.sere.core._
import com.googlecode.sere.util._
import scala.util.matching.Regex.Match
import com.googlecode.sere.http.NotFound
import collection.immutable.LinearSeq

/**
 * Implements a generic URL registry, agnostic about the mapped type
 */
abstract class GenericUrlRegistry[T] extends Logging {
	
  private val impl = scala.collection.mutable.Map.empty[Regex, T]

  def register(url: String, em: T) = {
		val regex = convertToRegex(url)
		impl(regex) = em
		logger.info("URL registered: {} as regex: {}", url, regex)
	}
    
  private def convertToRegex(s: String): Regex = {
		val (regex, params) = convertToRegex(s, "", List())
		new Regex(regex + '$', params: _*)
	}

 	private def convertToRegex(s: String, prefix: String, seq: LinearSeq[String]): 
 	    (String, Seq[String]) = {
		val regex = """\{([-a-z0-9_]+?)\}""".r
		regex.findFirstMatchIn(s) match {
      case Some(m) =>
        val rta = prefix + escape(s.substring(0, m.start)) + "(.+?)"
        convertToRegex(s.substring(m.end), rta, m.group(1) +: seq)
      case None =>
        (prefix + escape(s), seq.reverse)
      }
	}
  
	private def escape(in: String): String = in match {
		case "" => ""
		case _ => java.util.regex.Pattern.quote(in)
	}

	def matchUrl(s: String): (T, Map[String, String]) = {
		logger.debug("Trying to match {} against {} url patterns", s, impl.size)
		for ((regex, t) <- impl) {
			// First match should be the longest
			regex.findFirstMatchIn(s) match {
				case Some(matchh) =>
          logger.debug("Url matched by " + regex)
          val params = getGroups(matchh)
          logger.debug("Matched resource type: {}", t)
          logger.debug("Matched parameters " + params)
          return (t, params)
				case None =>
				  	// We continue trying
			}
		}
		logger.debug("{} not matched", s)
		(nullValue, Map())
	}
 
 	private def getGroups(theMatch: Match) =
		Map(theMatch.groupNames.toList map {name => (name, theMatch.group(name))} :_*)
  
  protected val nullValue: T
}